package com.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;

import com.stonehenge.backend.tools.geo.BasicPoint;

import org.apache.poi.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import com.google.common.collect.Lists;
import com.stonehenge.backend.tools.StaticToolit;
import com.stonehenge.backend.tools.data.GeneratorSID;

/**
 * 小工具
 * @author FU, lingjin Administrator
 * @Date 2016年9月4日
 */
public class Toolkit {
    private static final Logger logger = LoggerFactory.getLogger(Toolkit.class);

    private static final double EARTH_RADIUS = 6378137.0;//地球半径(米)

    static Random rn = new Random();
    
    public String getIpAddr(javax.servlet.http.HttpServletRequest request ){
        try {
            String ip = request.getHeader("X-Real-IP");
            if (!com.utils.StringUtil.empty(ip) && !"unknown".equalsIgnoreCase(ip)) {
                return ip;
            }
            ip = request.getHeader("X-Forwarded-For");
            if (!com.utils.StringUtil.empty(ip) && !"unknown".equalsIgnoreCase(ip)) {
                // 多次反向代理后会有多个IP值，第一个为真实IP。
                int index = ip.indexOf(',');
                if (index != -1) {
                    return ip.substring(0, index);
                } else {
                    return ip;
                }
            } else {
                return "127.0.0.1";
                // 有可能是ipv6
                //return request.getRemoteAddr();
            }
        } catch (Exception e) {
            return "127.0.0.1";
        }
    }
    
   public String getStringRandom(int length) {
        String val = "";
        Random random = new Random();
        //参数length，表示生成几位随机数
        // TODO 早期用户让邀请码简单一点，因此字母的占比不超过3个
        int charCnt = 0;
        for(int i = 0; i < length; i++) {
            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            //输出字母还是数字
            if( charCnt < 3 && "char".equalsIgnoreCase(charOrNum) ) {
                //输出是大写字母还是小写字母
                int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                while(true) {
                	int v = random.nextInt(26) + temp;
                	if (v == 79 || v == 108 || v == 111 || v == 73 || v == 105) {
                		// ignore o,O,l,I,i
                	} else {
                		val += (char)(v);
                        charCnt++;
                		break;
                	}
                }
            } else {
                val += String.valueOf(2+random.nextInt(8));
            }
        }
        return val;
    }
   
   
   public int age(Long time){
       if(time == null || time < 1L){
           return  18;
       }else{
           return (int) Math.ceil((SystemClock.now()/1000L - time)/60/60/24/365.0);
       }

   }

    /**
     * 防止误差，输入输出是单位是分，整型
     * 如果返回是null，说明传入的参数出错了d
     * @param amount
     * @param n
     * @return
     */
    public static Integer[] resortLuckyMoney(int amount, int n) {
        
        if(amount<n)
            return null;
        amount = amount-n;
        Integer[] list = new Integer[n];
        
        Float[] w = new Float[n];
        float wMax = 0f;
        for (int i = 0; i < n; i++) {
            list[i] = 0;
            float w0 = rn.nextFloat() * rn.nextFloat() + 0.01f;
            if(rn.nextFloat()<0.05)
                w0 = rn.nextFloat()*0.01f/n+0.01f; 
            wMax += w0;
            w[i] = w0;
        }
        if (amount <= 1 || wMax < 0.01)
            list[rn.nextInt(n)] = amount;
        else {
            int total = 0;
            for (int i = 0; i < n; i++) {
                int testAmount = (int) (amount * w[i] / wMax);                               
                if (testAmount + total > amount) {
                    testAmount = amount - total;
                    list[i] = testAmount;
                    total += testAmount;
                    break;
                } else {
                    list[i] = testAmount;
                    total += testAmount;
                }
                if (i == n - 1) {
                    int diff = amount - total;
                    list[i] += diff;
                }
            }
        }

        for (int i = 0; i < list.length; i++) {
            list[i] += 1;
        }
        return list;
    }

    /**
     * 获取文件后缀名
     */
    public static String getFileExt(String filepath) {
        if(StaticToolit.isEmpty(filepath)) {
            return "";
        }
        return filepath.substring(filepath.lastIndexOf(".") + 1);
    }
    
    /**
     * 判定是否是手机号
     * @param mobile
     * @return
     */
    public static boolean isMobile(String mobile) {
        if(StaticToolit.isEmpty(mobile) || mobile.length()!=11) {
            return false;
        }
        return Pattern.matches("^1[3|4|5|6|7|8|9][\\d]{9}$", mobile);
    }
    
    public static String distance_name(int distance) {
        if(distance < 500 ){
            return "<500m";
        }else if ( distance < 1000){
            return Math.round(distance) + "m";
        }else{
            return Math.round(distance/1000) + "km";
        }
    }

	/**
	 * 角度弧度计算公式 rad:(). <br/>
	 * 
	 * 360度=2π π=Math.PI
	 * 
	 * x度 = x*π/360 弧度
	 * 
	 * @author chiwei
	 * @param d
	 * @return
	 * @since JDK 1.6
	 */
	private static double getRadian(double degree) {
		return degree * Math.PI / 180.0;
	}

	/**
	 * 依据经纬度计算两点之间的距离 GetDistance:(). <br/>
	 * 
	 * 
	 * @author chiwei
	 * @param lat1
	 *            1点的纬度
	 * @param lng1
	 *            1点的经度
	 * @param lat2
	 *            2点的纬度
	 * @param lng2
	 *            2点的经度
	 * @return 距离 单位 米
	 * @since JDK 1.6
	 */
	public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
		double radLat1 = getRadian(lat1);
		double radLat2 = getRadian(lat2);
		double a = radLat1 - radLat2;// 两点纬度差
		double b = getRadian(lng1) - getRadian(lng2);// 两点的经度差
		double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1)
				* Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
		s = s * EARTH_RADIUS;
		return s * 1000;
	}
    
	
	private static final double PI = 3.1415926535898;
	
	/**
	 * 计算附近10公里 
	 * @param lat
	 * @param lng
	 * @return
	 */
	public static String getRandGPS(double lat, double lng) {
		return getRandGPS(lat,lng,10000.0);
		//System.out.println(lat1+","+lng1);
		//System.out.println(getDistance(lat,lng,lat1,lng1));
	}
	
	public static String getRandGPS(double lat, double lng,double distance) {
		double lat1,lng1,brg;
		double dist = 0;
		double rad360 = 2 * PI;
		double maxdist = distance;
		maxdist = maxdist / EARTH_RADIUS;
		double startlat = lat * PI / 180.0;
		double startlon = lng * PI / 180.0;
		double cosdif = Math.cos(maxdist) - 1;
		double sinstartlat = Math.sin(startlat);
		double cosstartlat = Math.cos(startlat);
		dist = Math.acos((new Random().nextDouble() * cosdif + 1));
		brg = rad360 * new Random().nextDouble();
		lat1 = Math.asin(sinstartlat * Math.cos(dist) + cosstartlat * Math.sin(dist) * Math.cos(brg));
		lng1 = normalizeLongitude(startlon * 1 + Math.atan2(Math.sin(brg) * Math.sin(dist) * cosstartlat, Math.cos(dist) - sinstartlat * Math.sin(lat)))* 180 / Math.PI;
		lat1 = lat1 * 180 / Math.PI;
		return lat1+","+lng1;
	}
	
	private static double normalizeLongitude(double lon) {
		double n = PI;
		if (lon > n) {
			lon = lon - 2 * n;
		} else if (lon < -n) {
			lon = lon + 2 * n;
		}
		return lon;
	}
    private static double padZeroRight(double s) {
		double sigDigits = 8;
		s = Math.round(s * Math.pow(10, sigDigits)) / Math.pow(10, sigDigits);
		return s;
	}
    
    /**
     * 精简的userid生成器
     * @param maxID
     * @param localIP
     * @param ipset
     * @return
     */    
    public static long newUserID(long maxID, String localIP, Set<String> ipset) {
        
        int ip = rn.nextInt(89) + 10;        
        int index = -1;
        if (localIP == null || localIP == ""){
            List<String> iplist = StaticToolit.convSet2List(ipset);
            index = iplist != null ? iplist.indexOf(localIP) : -1;
            if (index < 0)
                index = Math.abs(localIP.hashCode());            
            ip = index % 19;
        }

        long start = SystemClock.now();
        long time = start - 1472526222000L;
        int timeInt = (int) (time % 43L);

        if (maxID <= 1L)
            maxID = Math.abs(GeneratorSID.getUUID().hashCode() % 1000);
        else
            maxID += 10000L;

        long rand = maxID * 19 + ip;
        rand = rand * 43L + timeInt + 2000000L;
        if (rand < 0L)
            return ((rand + Long.MAX_VALUE) % 1000000L + 1000000L);
        
        return rand;
    }

    public static int getWordCount(String s) {
        if (null == s) {
            return 0;
        }
        int length = 0;
        for(int i = 0; i < s.length(); i++) {
            int ascii = Character.codePointAt(s, i);
            if(ascii >= 0 && ascii <=255)
                length++;
            else
                length += 2;

        }
        return length;
    }


    public static String newGroupID(String consumeids,int mile){
        if(consumeids==null)
            return null;        
        List<String> consumeids_list = StaticToolit.string2List(consumeids, ",");
        Collections.sort(consumeids_list);
        String consumeids2= StaticToolit.list2String(consumeids_list, "-");
        return consumeids2+"_"+mile;        
    }

    
    public static boolean _isUTF8(byte[] rawtext) {
        int score = 0;
        int i, rawtextlen = 0;
        int goodbytes = 0, asciibytes = 0;
        // Maybe also use UTF8 Byte Order Mark: EF BB BF
        // Check to see if characters fit into acceptable ranges
        rawtextlen = rawtext.length;
        for (i = 0; i < rawtextlen; i++) {
         if ((rawtext[i] & (byte) 0x7F) == rawtext[i]) { 
          // 最高位是0的ASCII字符
          asciibytes++;
          // Ignore ASCII, can throw off count
         } else if (-64 <= rawtext[i] && rawtext[i] <= -33
           //-0x40~-0x21
           && // Two bytes
           i + 1 < rawtextlen && -128 <= rawtext[i + 1]
           && rawtext[i + 1] <= -65) {
          goodbytes += 2;
          i++;
         } else if (-32 <= rawtext[i]
           && rawtext[i] <= -17
           && // Three bytes
           i + 2 < rawtextlen && -128 <= rawtext[i + 1]
           && rawtext[i + 1] <= -65 && -128 <= rawtext[i + 2]
           && rawtext[i + 2] <= -65) {
          goodbytes += 3;
          i += 2;
         }
        }
        if (asciibytes == rawtextlen) {
         return false;
        }
        score = 100 * goodbytes / (rawtextlen - asciibytes);
        // If not above 98, reduce to zero to prevent coincidental matches
        // Allows for some (few) bad formed sequences
        if (score > 98) {
         return true;
        } else if (score > 95 && goodbytes > 30) {
         return true;
        } else {
         return false;
        }
    }
        
    
    static public String _filterOffUtf8Mb4(String text) throws UnsupportedEncodingException {  
        byte[] bytes = text.getBytes("UTF-8");  
        ByteBuffer buffer = ByteBuffer.allocate(bytes.length);  
        int i = 0;  
        while (i < bytes.length) {  
            short b = bytes[i];  
            if (b > 0) {  
                buffer.put(bytes[i++]);  
                continue;  
            }  
            b += 256;  
            if ((b ^ 0xC0) >> 4 == 0) {  
                buffer.put(bytes, i, 2);  
                i += 2;  
            }  
            else if ((b ^ 0xE0) >> 4 == 0) {  
                buffer.put(bytes, i, 3);  
                i += 3;  
            }  
            else if ((b ^ 0xF0) >> 4 == 0) {  
                i += 4;  
            }  
        }  
        buffer.flip();  
        return new String(buffer.array(), "utf-8");  
    }  
    
    @Deprecated
    public static String toMD5(String plainText) {
        try {
           //生成实现指定摘要算法的 MessageDigest 对象。
           MessageDigest md = MessageDigest.getInstance("MD5");  
           //使用指定的字节数组更新摘要。
           md.update(plainText.getBytes());
           //通过执行诸如填充之类的最终操作完成哈希计算。
           byte b[] = md.digest();
           //生成具体的md5密码到buf数组
           int i;
           StringBuffer buf = new StringBuffer("");
           for (int offset = 0; offset < b.length; offset++) {
             i = b[offset];
             if (i < 0)
               i += 256;
             if (i < 16)
               buf.append("0");
             buf.append(Integer.toHexString(i));
           }
           System.out.println("32位: " + buf.toString());// 32位的加密
           System.out.println("16位: " + buf.toString().substring(8, 24));// 16位的加密，其实就是32位加密后的截取
           return buf.toString();
        } 
        catch (Exception e) {
          e.printStackTrace();
        }
        return null;
    }
    
    
	/**
	 * 获取充值订单号
	 * @return
	 */
    public static String newTradeNo(){
    	StringBuffer tradeNo = new StringBuffer(String.valueOf(SystemClock.now()/1000).substring(2));
    	//StringBuffer tradeNo = new StringBuffer(new SimpleDateFormat("ddHHmmss").format(new Date(SystemClock.now())));
    	// 获得随机数  
    	double pross = (1 + new Random().nextDouble()) * Math.pow(10, 8);  
    	tradeNo.append(String.valueOf(pross).substring(2, 18))   ;  
    	return tradeNo.toString();
    }
    
    public static String getAlipayDateString(){
    	return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(SystemClock.now()));
    }
    
    
    public static Toolkit one = new Toolkit();
    

    /**
     *根据经纬度获取省市区
     * @param log
     * @param lat
     * @return
     */
    public static String getAdd(String loc){
        //lat 小  log  大
        //参数解释: 纬度,经度 type 001 (100代表道路，010代表POI，001代表门址，111可以同时显示前三项)
        String urlString = "http://api.map.baidu.com/geocoder/v2/?location="+loc+"&output=json&ak=co1mC9vcPKDEMyqI0UQV2eqvrAQ8mcv1";
        String res = "";
        try {
            URL url = new URL(urlString);
            java.net.HttpURLConnection conn = (java.net.HttpURLConnection)url.openConnection();
            conn.setDoOutput(true);
            conn.setRequestMethod("POST");
            java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream(),"UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                res += line+"\n";
            }
            in.close();
            System.out.println(res);
            //解析结果
            JSONObject jsonObject = JSONObject.parseObject(res);
            JSONObject result= jsonObject.getJSONObject("result");
            JSONObject address = result.getJSONObject("addressComponent");
            res = address.getString("province");//返回省市区
        } catch (Exception e) {
        	e.printStackTrace();
            return null;
        }	
        return res;
    }
 


    /// 角度数转换为弧度公式
    private static double radians(double d) {
        return d * Math.PI / 180.0;
    }
    /// 弧度转换为角度数公式
    private static double degrees(double d) {
        return d * (180 / Math.PI);
    }

    /// 以一个经纬度为中心计算出四个顶点
    public static BasicPoint[] getDegreeCoordinates(BasicPoint Degree1, double distance) {
        double dlng = 2 * Math.asin(Math.sin(distance / (2 * EARTH_RADIUS)) / Math.cos(Degree1.pt.getLat()));
        dlng = degrees(dlng);//一定转换成角度数  原PHP文章这个地方说的不清楚根本不正确 后来lz又查了很多资料终于搞定了
        double dlat = distance / EARTH_RADIUS;
        dlat = degrees(dlat);//一定转换成角度数
        return new BasicPoint[] { new BasicPoint(Degree1.pt.getLon() - dlng, Degree1.pt.getLat() - dlat),
                new BasicPoint(Degree1.pt.getLon() + dlng, Degree1.pt.getLat() + dlat) };
    }

    
    public static void main(String[] args) throws UnsupportedEncodingException {
    	// 	40.032722  120.77827	31.602153
    	
    	//System.out.println(getDistance(40.032722, 116.34116, 31.602153, 120.77827));
    	/// 79 111  108
//    	System.out.println(Toolkit.getAdd("37.76476829781919,114.5006011148118"));
//    	System.out.println(Toolkit.getDistance(23.137578, 113.355208, 23.085209771365935,113.33417952939492));
    	System.out.println(Toolkit.getRandGPS(31.74873203064133,120.7994776081254));
    	for(int i = 0 ; i < 100; i++) {
    		System.out.println(Toolkit.getRandGPS(31.74873203064133,120.7994776081254));
    		//Toolkit.getRandGPS(23.137578,113.355208);
    	}
    		
//    		System.out.println(newTradeNo());
    		
//    	System.out.println(Toolkit.one.loginManForTxt("changlong", "478856"));
//    	System.out.println(Toolkit.one.loginManForTxt("13211111111", ""));
//    	System.out.println(Toolkit.one.getCallBackIP());
    	/*
    	System.out.println(new Toolkit().getPTName(10));
    	
    	System.out.println(new Toolkit().getPTLevel(13000));
    	System.out.println(new Toolkit().getPoint("白银会员"));

    	/*
    	System.out.println(SystemClock.now()/1000);
    	System.out.println(String.valueOf(SystemClock.now()/1000).substring(2));
    	System.out.println(one.newTradeNo());
    	/*
    	Map<String,Object> obj = Maps.newHashMap();
    	obj.put("prodid", 1);
    	obj.put("detailid", 1);
    	obj.put("productdetail", "sss");
    	obj.put("note", null);
    	obj.put("amount", 8.88);
    	obj.put("address", "广济南路270号");
    	obj.put("province", "江苏省");
    	obj.put("city", "苏州市");
    	obj.put("name", "王小二");
    	obj.put("mobile", 118121020);
    	obj.put("phone", 823923);
    	System.out.println(new JSONObject(obj));
    	System.out.println(Toolkit.one.getLevelExpr(2));
    	String pattern = "\\d{6,6}";
    	 
        // 创建 Pattern 对象
        Pattern r = Pattern.compile(pattern);
   
        // 现在创建 matcher 对象
        Matcher m = r.matcher("128903");
        System.out.println(m.find());
        System.out.println(newUserID(100000L, "10.10.10.1", ServerInfo.AllowIPs_Service));

        //System.out.println(newUserID(100000L, "10.10.10.1", ServerInfo.AllowIPs_Service));

        //System.out.println((-100L + Long.MAX_VALUE) % 1000000L);
        //System.out.println((-100L + -1) * 1000000L + 1000000L);
        //String emojiString2=new String("\ud83d\ude00\u2602");

        //System.out.println(_isUTF8("sff.235235".getBytes()));
        //System.out.println(_isUTF8("我".getBytes()));
        //System.out.println(_isUTF8("\u8776dd".getBytes()));
        //System.out.println(_isUTF8(emojiString2.getBytes()));
        
        
        //System.out.println(_filterOffUtf8Mb4("sff.235235"));
        //System.out.println(_filterOffUtf8Mb4("我"));
        //System.out.println(_filterOffUtf8Mb4("\u8776 dd"));
        //System.out.println(_filterOffUtf8Mb4(emojiString2));
        
//        System.out.println(hasEmoji("sff.235235"));
//        System.out.println(hasEmoji("我"));
//        System.out.println(hasEmoji("\u8776 dd"));
//        System.out.println(hasEmoji(emojiString2));
//        System.out.println("isMobile=" + isMobile("13601011234"));

        Integer[] resortLuckyMoney = resortLuckyMoney(100,8);

        int n=0;
        for (Integer integer : resortLuckyMoney) {
            n+=integer;            
        }
        System.out.println("lucky=" + Arrays.asList(resortLuckyMoney) + " total=" + n);
        */

    }
}


